package cn.codeartist.springboot.component.util;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 字符串Optional类
 *
 * @author 艾江南
 * @date 2019/3/20
 */
public final class OptionalString {

    private static final OptionalString EMPTY = new OptionalString();

    private final String value;

    private OptionalString() {
        this.value = null;
    }

    public static OptionalString empty() {
        return EMPTY;
    }

    private OptionalString(String value) {
        if (isEmpty(value)) {
            throw new NullPointerException();
        }
        this.value = value;
    }

    public static OptionalString of(String value) {
        return new OptionalString(value);
    }

    public static OptionalString ofNullable(String value) {
        return isEmpty(value) ? empty() : of(value);
    }

    public String get() {
        if (isEmpty(value)) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public boolean isPresent() {
        return !isEmpty(value);
    }

    public void ifPresent(Consumer<? super String> consumer) {
        if (!isEmpty(value)) {
            consumer.accept(value);
        }
    }

    public OptionalString filter(Predicate<? super String> predicate) {
        Objects.requireNonNull(predicate);
        if (isEmpty(value)) {
            return this;
        } else {
            return predicate.test(value) ? this : empty();
        }
    }

    public <T> Optional<T> map(Function<? super String, T> mapper) {
        Objects.requireNonNull(mapper);
        if (isEmpty(value)) {
            return Optional.empty();
        } else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    public Optional flatMap(Function<? super String, Optional> mapper) {
        Objects.requireNonNull(mapper);
        if (isEmpty(value)) {
            return Optional.empty();
        } else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    public String orElse(String other) {
        return !isEmpty(value) ? value : other;
    }

    public String orElseGet(Supplier<? extends String> other) {
        return !isEmpty(value) ? value : other.get();
    }

    public <X extends Throwable> String orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (!isEmpty(value)) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    private static boolean isEmpty(String string) {
        return string == null || string.trim().isEmpty();
    }
}
